#include <linux/sched.h>
#include <linux/elf.h>
#include <linux/kernel.h>

#include <sys/stat.h>
#include <errno.h>
#include <string.h>

int do_execve(unsigned long *eip, long tmp, char *filename, char **argv, char **envp)
{
    struct elfhdr elf_ex;
    struct m_inode *inode;
    struct buffer_head *bh;
    struct elf_phdr *elf_phdata;
    int i;
    int e_uid, e_gid;

    if ((0xffff & eip[1]) != 0x000f)
        panic("execve called from supervisor mode");

    inode = namei(filename);
    if (!inode)
        return -ENOENT;
    
    if (!S_ISREG(inode->i_mode)) {  // 必须是普通文件
        return -EACCES;
    }
    
    i = inode->i_mode;
    e_uid = (i & S_ISUID) ? inode->i_uid : current->euid;
    e_gid = (i & S_ISGID) ? inode->i_gid : current->egid;
    if (current->euid == inode->i_uid)
        i >>= 6;
    else if (current->egid == inode->i_gid)
        i >>= 3;
    if (!(i & 1) && !((inode->i_mode & 0111) && suser()))   // 必须是可执行文件
        return -ENOEXEC;

    // 读取文件头和程序头
    bh = bread(inode->i_dev, inode->i_zone[0]);
    if (!bh)
        return -EACCES;
    
    elf_ex = *((struct elfhdr *)bh->b_data);

    if (elf_ex.e_ident[0] != 0x7f ||
        strncmp((char *)&elf_ex.e_ident[1], "ELF",3) != 0)
        return -ENOEXEC;

    if(elf_ex.e_type != ET_EXEC || elf_ex.e_machine != EM_386)
        return -ENOEXEC;

    elf_phdata = (struct elf_phdr *)(bh->b_data + elf_ex.e_phoff);
    printk("Type:       0x%x\n", elf_phdata->p_type);
    printk("Offset:     0x%x\n", elf_phdata->p_offset);
    printk("VirtAddr:   0x%x\n", elf_phdata->p_vaddr);
    printk("PhysAddr:   0x%x\n", elf_phdata->p_paddr);
    printk("FileSiz:    0x%x\n", elf_phdata->p_filesz);
    printk("MemSiz:     0x%x\n", elf_phdata->p_memsz);
    printk("Flg:        0x%x\n", elf_phdata->p_flags);
    printk("Align:      0x%x\n", elf_phdata->p_align);

    current->euid = e_uid;
    current->egid = e_gid;
    return 0;
}
